﻿using OPCAutomation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using TYBase;

namespace TYPLC.Kep
{
    public class KepConnector : IPLCConnector, IDisposable
    {
        private OPCServer m_objOPCServer;
        private OPCGroups ObjOPCGroups;
        private string m_serverName = "KEPware.KEPServerEx.V6";
        private const string Node = "127.0.0.1";
        public int clientHandle { get; set; }
        private List<TYOpcGroup> listGroup = new List<TYOpcGroup>();
        private List<GroupPara> listCommGroup = new List<GroupPara>();

        private object lockObj = new object();


        public override bool AddGroup(GroupPara groupPara)
        {
            {

                lock (lockObj)
                {
                    if (listCommGroup.Count(x => x.GroupName == groupPara.GroupName) >= 1)
                    {
                        Log.I($"PLC-  has GroupName {groupPara.GroupName}");
                        return false;
                    }
                    listCommGroup.Add(groupPara);
                }
                return true;
            }
        }

        public override bool Connect()
        {
            try
            {
                Log.I("Connect PLC START!");
                IsConnected = false;
                bool isGet = false;
                m_objOPCServer = new OPCServer();
                m_objOPCServer.ServerShutDown += M_objOPCServer_ServerShutDown;
                Log.I("Connect PLC START! 2");
                ObjOPCGroups = m_objOPCServer.OPCGroups;
                Log.I("Connect PLC START! 3");
                object serverList = m_objOPCServer.GetOPCServers(Node);

                Log.I("Connect PLC START!  4");
                foreach (string turn in (Array)serverList)
                {
                    if (turn.ToUpper().Contains("KEPWARE"))
                    {
                        m_serverName = turn;
                        isGet = true;
                        Log.I($"PLC 获取本地OPC服务 :{m_serverName}!");
                        break;
                    }
                }
                if (isGet == false)
                {
                    Log.I("本地OPC无服务!");
                    return false;
                }
                Log.I($"Connect PLC  {m_serverName}!");
                m_objOPCServer.Connect(m_serverName, Node);
                IsConnected = true;
                return true;
            }
            catch (Exception ex)
            {
                Log.E(ex, $"Connect PLC!");
                return false;
            }
        }

        private void M_objOPCServer_ServerShutDown(string Reason)
        {
            IsConnected = false;
        }

        public override bool GetTagType(string groupName, int index, out PLCDataType dataType)
        {
            dataType = PLCDataType.BYTE;
            try
            {
                var groupItem = listCommGroup.FirstOrDefault(x => x.GroupName == groupName);
                if (groupItem == null)
                    return false;
                var tag = groupItem.ListTag.FirstOrDefault(x => x.DataIndex == index);
                if (tag == null)
                    return false;
                dataType = tag.DataType;
                return true;
            }
            catch (Exception ex)
            {
                Log.E(ex, $"PLC: GetTagType error-->{groupName}-->{index}");
                return false;
            }
        }

        public override bool Read(string groupName, int index, out object value)
        {
            if (IsConnected == false)

            {
                value = null;
                return false;
            }
            try
            {
                value = null;
                TYOpcItem item = GetOpcItem(groupName, index);
                if (item == null || item?.OpcItem == null)
                    return false;
                object Qualities; object TimeStamps;//同步读的临时变量：值、质量、时间戳
                item.OpcItem.Read(2, out value, out Qualities, out TimeStamps);//同步读，第一个参数只能为1或2
                                                                               //LogHelper.Info($"read over  ItemValues {value}");

                return true;
            }
            catch (Exception ex)
            {
                Log.E(ex, $"PLC:Read error-->{groupName}-->{index}");
                value = null;
                return false;
            }
        }

        public override bool Start()
        {
            if (IsConnected == false)
            {
                Log.I($"初始化异常!");
                return false;
            }
            for (int i = 0; i < listCommGroup.Count; i++)
            {
                var commGroup = listCommGroup[i];

                lock (lockObj)
                {
                    if (listGroup.Count(x => x.Name == commGroup.GroupName) >= 1)
                    {
                        Log.I($"PLC-  has GroupName {commGroup.GroupName}");
                        return false;
                    }
                    var ObjOPCGroup = ObjOPCGroups.Add(commGroup.GroupName);
                    List<TYOpcItem> listItem = new List<TYOpcItem>();
                    listGroup.Add(new TYOpcGroup
                    {
                        Name = commGroup.GroupName,
                        ListItem = listItem,
                        OpcGroup = ObjOPCGroup
                    });
                    if (commGroup.IsActive)
                    {
                        ObjOPCGroup.DataChange += ObjOPCGroup_DataChange;
                    }
                    for (int j = 0; j < commGroup.ListTag.Count; j++)
                    {
                        var address = commGroup.ListTag[j].Address;
                        clientHandle++;
                        var item = ObjOPCGroup.OPCItems.AddItem(address, clientHandle);
                        listItem.Add(new TYOpcItem
                        {
                            Address = address,
                            ClientHand = clientHandle,
                            Index = commGroup.ListTag[j].DataIndex,
                            OpcItem = item
                        });
                    }
                    ObjOPCGroup.UpdateRate = commGroup.CheckTimes;
                    ObjOPCGroup.IsActive = commGroup.IsActive;
                    ObjOPCGroup.IsSubscribed = true;

                }
            }

            return true;
        }

        public override bool Write(string groupName, int index, object value)
        {
            if (IsConnected == false)
                return false;
            try
            {
                TYOpcItem item = GetOpcItem(groupName, index);
                if (item == null || item?.OpcItem == null)
                    return false;
                System.Diagnostics.Debug.Print($"plc send {item.Address} {value}");
                item?.OpcItem?.Write(value);

                return true;
            }
            catch (Exception ex)
            {
                Log.E(ex, $"PLC:Write error-->{groupName}-->{index}-->{value}");
                return false;
            }
        }


        private TYOpcItem GetOpcItem(string groupName, int index)
        {
            if (IsConnected == false)
                return null;
            lock (lockObj)
            {
                var group = listGroup.FirstOrDefault(x => x.Name == groupName);
                if (group == null)
                    return null;
                return group.ListItem.FirstOrDefault(x => x.Index == index);
            }
        }
        private void ObjOPCGroup_DataChange(int TransactionID,
            int NumItems,
            ref Array ClientHandles,
            ref Array ItemValues,
            ref Array Qualities,
            ref Array TimeStamps)
        {
            lock (lockObj)
            {
                for (int i = 1; i <= NumItems; i++)
                {
                    int clientHand = Convert.ToInt32(ClientHandles.GetValue(i));
                    foreach (var groupItem in listGroup)
                    {
                        foreach (var tag in groupItem.ListItem)
                        {
                            if (clientHand == tag.ClientHand)
                            {
                                FireDatChange(groupItem.Name, tag.Index, ItemValues.GetValue(i));
                            }
                        }
                    }
                }
            }
        }

        public void Dispose()
        {
            IsRun = false;
            m_objOPCServer.OPCGroups.RemoveAll();
            m_objOPCServer.Disconnect();
        }
    }
}

